// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT

#include "constants_priv.h"

#define ICC_IAR1_EL1 S3_0_C12_C12_0

.set CPACR_EL1_FPEN_NO_TRAP, (3 << 20)

// See ARM ARM Table D1.10.2 for more details.
.set CURRENT_EL_EL0_BASE,    0x000
.set CURRENT_EL_ELx_BASE,    0x200
.set LOWER_EL_ARCH64_BASE,   0x400
.set LOWER_EL_ARCH32_BASE,   0x600
.set SYNC_EXC_OFFSET,        0x000
.set IRQ_EXC_OFFSET,         0x080
.set FIQ_EXC_OFFSET,         0x100
.set SERROR_EXC_OFFSET,      0x180

.set SPSR_M_BIT_ARCH32,      0x010

.set GICC_BASE,              0x800001000
.set GICC_IAR_OFFSET,        0xc

.macro movlit reg, literal
mov \reg, #((\literal) & 0xffff)
.ifne (((\literal) >> 16) & 0xffff)
movk \reg, #(((\literal) >> 16) & 0xffff), lsl #16
.endif
.ifne (((\literal) >> 32) & 0xffff)
movk \reg, #(((\literal) >> 32) & 0xffff), lsl #32
.endif
.ifne (((\literal) >> 48) & 0xffff)
movk \reg, #(((\literal) >> 48) & 0xffff), lsl #48
.endif
.endm

// Define new function 'name'.
// Local label '1:' used by other macros to compute offsets.
.macro FUNCTION name
    .global \name
    .type \name, STT_FUNC
    \name:
    1:
.endm

// Adjusts current text location to position at a specific exception handler.
.macro exception_vector base offset
    2:
    .skip (\base + \offset) - (2b - 1b)
.endm

// test_complete Signals test completion by writing value (0-255) to
// EXIT_TEST_ADDR. EXIT_TEST_ADDR is monitored by the test routine for
// access - indicating test completion.
.macro test_complete value=0 arch=aarch64
    .if \value > 255 || \value < 0
        .err
    .endif

    .ifeqs "\arch", "aarch64"
        mov x0, EXIT_TEST_ADDR
        .if \value == 0
            str xzr, [x0]
        .else
            mov x1, \value
            str x1, [x0]
        .endif
    .else
        .ifeqs "\arch", "aarch32"
            .word 0xE3A01000 + \value  // aarch32: movw r1, \value
            .word 0xE30F0000           // aarch32: movw r0, [lower]EXIT_TEST_ADDR
            .word 0xE34000FF           // aarch32: movw r0, [upper]EXIT_TEST_ADDR
            .word 0xE5801000           // aarch32: str  r0, [r1]
        .else
            .error "Unsupported architecture"
        .endif
    .endif
.endm

// Drop exception level from EL1 to EL0 and, if requested, switch execution
// state.
.macro drop_to_el0 arch=aarch64
    .ifeqs "\arch", "aarch32"
        mov x0, SPSR_M_BIT_ARCH32  // for aarch32, set PSTATE to AARCH32
        msr spsr_el1, x0           // and clear everything else upon eret.
    .else
        .ifnes "\arch", "aarch64"  // accept default architecture, do nothing.
            .error "Unsupported architecture"
        .endif
    .endif

    ldr w0, 2f               // resume execution at label '2' ahead
    msr elr_el1, x0          // store exception link register
    eret                     // resume at el0 using specified arch.
    2:
    .word 2b - 1b + 4
.endm

.text

// Test vcpu_resume.
FUNCTION vcpu_resume_start
    test_complete
FUNCTION vcpu_resume_end

// Test vcpu_read_state and vcpu_write_state.
FUNCTION vcpu_read_write_state_start
    add x1, x1, #1
    add x2, x2, #2
    add x3, x3, #3
    add x4, x4, #4
    add x5, x5, #5
    add x6, x6, #6
    add x7, x7, #7
    add x8, x8, #8
    add x9, x9, #9
    add x10, x10, #10
    add x11, x11, #11
    add x12, x12, #12
    add x13, x13, #13
    add x14, x14, #14
    add x15, x15, #15
    add x16, x16, #16
    add x17, x17, #17
    add x18, x18, #18
    add x19, x19, #19
    add x20, x20, #20
    add x21, x21, #21
    add x22, x22, #22
    add x23, x23, #23
    add x24, x24, #24
    add x25, x25, #25
    add x26, x26, #26
    add x27, x27, #27
    add x28, x28, #28
    add x29, x29, #29
    add x30, x30, #30

    add sp, sp, #64
    cmp sp, #128 // Set ZC bits of CPSR.

    test_complete
FUNCTION vcpu_read_write_state_end

// Test vcpu_interrupt.
FUNCTION vcpu_interrupt_start
    msr daifclr, #2
    b .

    exception_vector CURRENT_EL_ELx_BASE, IRQ_EXC_OFFSET
    test_complete
FUNCTION vcpu_interrupt_end

// Test guest_set_trap using a memory-based trap.
FUNCTION guest_set_trap_start
    mov x0, TRAP_ADDR
    str xzr, [x0]
    test_complete
FUNCTION guest_set_trap_end

// Test wfi instruction handling.
FUNCTION vcpu_wfi_start
    // Setup the virtual timer by:
    // 1. Setting the compare value to 0.
    // 2. Enabling the virtual timer.
    msr cntv_cval_el0, xzr
    mov x0, 1
    msr cntv_ctl_el0, x0

    wfi
    test_complete
FUNCTION vcpu_wfi_end

// Test wfi handling with pending interrupt in LR (GICv2).
FUNCTION vcpu_wfi_pending_interrupt_gicv2_start
    msr daifclr, #2
    b .

    exception_vector CURRENT_EL_ELx_BASE, IRQ_EXC_OFFSET
    movlit x0, GICC_BASE
    // Acknowledge interrupt by reading IAR.
    ldr w1, [x0, GICC_IAR_OFFSET]
    wfi
    test_complete
FUNCTION vcpu_wfi_pending_interrupt_gicv2_end

// Test wfi handling with pending interrupt in LR (GICv3).
FUNCTION vcpu_wfi_pending_interrupt_gicv3_start
    msr daifclr, #2
    b .

    exception_vector CURRENT_EL_ELx_BASE, IRQ_EXC_OFFSET
    movlit x0, GICC_BASE
    // Acknowledge interrupt by reading IAR.
    mrs x1, ICC_IAR1_EL1
    dsb sy
    wfi
    test_complete
FUNCTION vcpu_wfi_pending_interrupt_gicv3_end

// Test wfi instruction handling.
// Execution of WFI at EL0 on AARCH32 is propagated to EL1 / AARCH64.
FUNCTION vcpu_aarch32_wfi_start
    drop_to_el0 aarch32
    .word 0xE320F003         // aarch32: wfi
    test_complete 1 aarch32  // Fail, if instruction was executed
                             // without raising exception.

    exception_vector LOWER_EL_ARCH32_BASE, SYNC_EXC_OFFSET
    test_complete
FUNCTION vcpu_aarch32_wfi_end

// Test floating-point instruction handling with trapping between levels.
FUNCTION vcpu_fp_start
    drop_to_el0

    // Access vector registers.
    mov w0, 0xff
    dup v0.16b, w0
    test_complete

    // Handle EL1 floating-point trap.
    // This is interpreted as a Lower exception level (coming from EL0)
    // captured by AARCH64. See ARM ARM Table D1.10.2 for more details.
    exception_vector LOWER_EL_ARCH64_BASE, SYNC_EXC_OFFSET
    mov x0, CPACR_EL1_FPEN_NO_TRAP
    msr cpacr_el1, x0
    eret
FUNCTION vcpu_fp_end

// Test wfi instruction handling.
FUNCTION vcpu_aarch32_fp_start
    drop_to_el0 aarch32
    // Load double precision register d0 from address 0.
    // This should trigger floating point exception.
    .word 0xE3A00000         // aarch32: mov r0, 0
    .word 0xED900B00         // aarch32: vldr d0, [r0]
    test_complete 0 aarch32

    exception_vector LOWER_EL_ARCH32_BASE, SYNC_EXC_OFFSET
    mov x0, CPACR_EL1_FPEN_NO_TRAP
    msr cpacr_el1, x0
    eret
FUNCTION vcpu_aarch32_fp_end
